//
//$Id: fa_jflap.cpp 649 2011-02-18 18:30:44Z andrew.tsyganov $
#include "stdafx.h"
#include "src/heo/include/xml_file.h"
#include "src/heo/include/stream_visitor.h"
#include "include/fa.h"
#include "include/utils.h"

using namespace std;

struct JffState
{
    size_t id_;
    float x_;
    float y_;
    string name_;
    bool initial_;
    bool final_;

    static char const* get_Section() { return "/"; }

    void accept(meta::ostream_visitor& osv)
    {
        osv(VAR_(id));
        osv(VAR_(name), "");
        if (initial_)
            osv(VAR_(initial), false);
        if (final_)
            osv(VAR_(final), false);
    }

    void accept(xml::in& in)
    {
        in(VAR_(id));
        in(VAR_(name), "");
        initial_ = !xml::in(in, "initial").is_Null();
        final_   = !xml::in(in, "final").is_Null();
    }

    void accept(xml::out& out)
    {
        out(VAR_(id));
        out(VAR_(name), "");
        out(x_, "/x", 0.f);
        out(y_, "/y", 0.f);
        if (initial_)
            xml::out(out, "initial");
        if (final_)
            xml::out(out, "final");
    }
};

struct JffTransition
{
    size_t     from_;
    size_t     to_;
    Symbol  read_;

    static char const* get_Section() { return "/"; }

    template<class V>
    void accept(V& v)
    {
        v(from_, "/from", -1);
        v(to_,   "/to",   -1);
        v(read_, "/read", '\0');
    }
};

bool FA::load_Jflap(string const& file)
{
    clear();
    xml::file f;
    if (!f.load(file.c_str()))
        return false;
    f.parse();
    xml::in root(f, "structure");
    string type;
    root.visit(type, "/type", "");
    if ("fa" != type)
    {
        cout << "FA::load_Jflap ERROR: unknown type: '" << type << "' in " << file << endl;
        return false;
    }
    xml::in automaton(root, "automaton");
    if (automaton.is_Null())
    {
        cout << "FA::load_Jflap ERROR: <automaton> not found in " << file << endl;
        return false;
    }
    vector<JffState> jstate;
    vector<JffTransition> jtrans;
    for (rapidxml::xml_node<>* it = automaton.node_->first_node(); it; it = it->next_sibling())
    {
        if (!*it->name())
            continue;
        if (!strcmp("state", it->name()))
        {
            size_t id = jstate.size();
            jstate.resize(id + 1);
            JffState& js = jstate[id];
            xml::in state_element(it);
            state_element >> js;
        }
        else if (!strcmp("transition", it->name()))
        {
            size_t id = jtrans.size();
            jtrans.resize(id + 1);
            JffTransition& jt = jtrans[id];
            xml::in trans_element(it);
            trans_element >> jt;
            if ('\0' == jt.read_)
            {
                cout << "FA::load_Jflap ERROR: lambda in " << file << " isn't supported yet" << endl;
                return false;
            }
        }
        else
        {
            cout << "FA::load_Jflap WARNING: unknown tag '" << it->name() << "' in " << file << endl;
        }
    }
    
    ////:debug output
    //meta::ostream_visitor os(cout);
    //os.visit(jstate, "jff::state", "");
    //os.visit(jtrans, "jff::transition", "");

    for (vector<JffState>::const_iterator it = jstate.begin(); it != jstate.end(); ++it)
    {
        add_State(it->name_);
        if (it->initial_)
            set_Initial(it->name_);
        if (it->final_)
            set_Final(it->name_);
    }
    struct StateFinder
    {
        static ptrdiff_t find_State(vector<JffState> const& v, size_t id)
        {//:until C++0x jstate isn't accesible here :-(
            for (vector<JffState>::const_iterator it = v.begin(); it != v.end(); ++it)
            {
                if (it->id_ == id)
                    return it - v.begin();
            }
            return -1;
        }
    };
    for (vector<JffTransition>::const_iterator it = jtrans.begin(); it != jtrans.end(); ++it)
    {
        add_Symbol(it->read_);
        ptrdiff_t from  = StateFinder::find_State(jstate, it->from_);
        if (from < 0)
        {
            cout << "FA::load_Jflap ERROR: invalid transition from=" << it->from_ << " in " << file << endl;
            return false;
        }
        ptrdiff_t to    = StateFinder::find_State(jstate, it->to_);
        if (to < 0)
        {
            cout << "FA::load_Jflap ERROR: invalid transition to=" << it->to_ << " in " << file << endl;
            return false;
        }
        add_Transition(jstate[from].name_, it->read_, jstate[to].name_);
    }
    return true;
}

bool FA::save_Jflap(string const& file) const
{
    xml::file f;
    xml::out root(f, "structure", "1.0", "UTF-8", "no");
    string type = "fa";
    root.visit(type, "/type", "");
    xml::out automaton(root, "automaton");
    static const size_t radius = 300;
    size_t state_count = states_.size();
    if (state_count)
    {
        //:place along a circle
        double angle = 2 * Math_PI / state_count;
        double phase = -Math_PI / 2;
        for (size_t id = 0; id < state_count; ++id)
        {
            xml::out state_out(automaton, "state");
            float x = float(radius * cos(angle * id + phase));
            float y = float(radius * sin(angle * id + phase));
            JffState jstate = {id, x, y, states_[id], initial_[id] != false, final_[id] != false};
            state_out << jstate;
        }
    }
    for (size_t from = 0; from < delta_.size(); ++from)
    {
        for (size_t j = 0; j < alphabet_.size(); ++j)
        {
            for (Subset::enumerator to = delta_[from][j].first(); to != delta_[from][j].end(); ++to)
            {
                xml::out trans_out(automaton, "transition");
                JffTransition jtrans = {from, *to, alphabet_[j]};
                trans_out << jtrans;
            }
        }
    }
    return f.save(file.c_str());
}

//EOF!
